Skip to content

Suggestion (#7): ck-notation.js — additive CKN compiler over the shipped verb surface#8

Open
styk-tv wants to merge 1 commit into
mainfrom
feat/notation-compiler-suggestion
Open

Suggestion (#7): ck-notation.js — additive CKN compiler over the shipped verb surface#8
styk-tv wants to merge 1 commit into
mainfrom
feat/notation-compiler-suggestion

Conversation

@styk-tv

@styk-tv styk-tv commented Jul 4, 2026

Copy link
Copy Markdown
Member

Addresses #7. This is a suggestion in the direction agreed there — not a claim on the boundary. cklib is authoritative: adapt the API, rename, restructure, or reject any of it freely. It's offered as a working starting point so the discussion has concrete code to react to, and it's deliberately shaped to the maintainer's answers on #7.

What it is

An additive, separately-importable module — ck-notation.js at @conceptkernel/cklib/notation — that turns an instance-plane CKN expression into a construct on a live kernel, compiling only to verbs ck.js already ships.

  • compile(source) → plan — pure, I/O-free, an inspectable [{verb, payload}] dispatch plan (a caller can audit/authorize it before running).
  • assemble(handle, planOrSource) → { ok, urns, steps } — runs a plan (or a source string) through a ConceptKernel handle via its open do(verb, payload) form; {$ref} payload values resolve from the sealed ids of earlier steps.

Holds the #7 acceptance bar

  • Plan-emitter, not a graph — output is [{verb, payload}]; no RDF, quad store, or query language emitted. The L1 store-not-graph invariant stays true.
  • Zero authority — every step goes through the handle's governed dispatch; pgCK gates each. The plan is a proposal; the server decides. compile() resolves/evaluates/traverses nothing.
  • ck.js unchanged — the module sits strictly above ConceptKernel, using only its public surface.
  • Zero deps, offline-testablecompile() is pure; tests/smoke-notation.mjs runs over a mock transport via CK.activate, in the existing smoke-*.mjs style.

Scope — slice 1 (instance plane only)

χ/ρ→create · edges→link · τ→transition · π→verify/provenance. The genome plane (σ/α/γ → propose ▸ vote ▸ apply) is deferred: a genome-plane expression is rejected with a typed NotationError rather than pretended, and will degrade honestly to gov_plane_unavailable when that plane lands (mirroring the existing _gov path).

Two deliberate choices, open to your call

  • assemble runs each step via handle.do(verb, payload) (the open form) rather than the named conveniences, so the plan stays a uniform [{verb, payload}] and every step is inspectable/governed identically. Equivalent to the named-method calls; happy to switch to per-verb method dispatch if you prefer.
  • The compiler injects no type-specific field. Properties come only from the ρ strand as written, so a type's declared-required fields (e.g. a shape's target_kernel) are the author's to supply and the server rejects a missing one honestly — the compiler assumes nothing about any type.

Tests & wiring

npm test green across all suites (smoke-ck / store / client / derived / notation 27/27). package.json exports + files + check/test updated; staged as its own release cut per #7 — not folded into the v1.5.4 scoring byte-set. CHANGELOG under [Unreleased].

Sequencing per #7: orthogonal to the scoring loop; intended to land as its own cut after v1.5.4. No rush on merge — this is here to make the direction concrete and to hand the implementation to cklib to own.

…r the shipped verb surface (#7)

Proposed reference implementation in the direction agreed on #7. cklib is
authoritative on the boundary/API — adapt, rewrite, or reject freely.

- ck-notation.js: compile(source)→plan (pure, inspectable [{verb,payload}])
  + assemble(handle, planOrSource) over ConceptKernel.do(); instance plane
  only (χ/ρ→create, edges→link, τ→transition, π→verify/provenance); genome
  plane deferred (typed NotationError). Plan-emitter only — no RDF/quad/query
  surface; ck.js untouched; zero deps; injects no type-specific field.
- tests/smoke-notation.mjs: pure compile goldens + assemble over a mock
  transport (CK.activate), refs + honest degrade. 27/27; full suite green.
- package.json: ./notation export + files + check/test wired.
- CHANGELOG: Unreleased; its own cut, not folded into the scoring byte-set.
@styk-tv styk-tv added enhancement New feature or request P1 Priority 1 — this release labels Jul 4, 2026

@styk-tv styk-tv left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — direction and module shape accepted; it holds the #7 bar. Two wire fixes + one design call before cklib owns it as its own cut. Strong, precisely-scoped contribution — thank you.

I verified rather than took on faith:

✅ Holds the acceptance bar (checked, not assumed)

  • ck.js unchanged — confirmed by diff; the three core modules are byte-identical. Module uses only handle.do(verb, payload, opts).
  • Plan-emitter, not a graphcompile() returns [{verb, payload, ref?}]; no RDF/quad/query emitted. The AST is a parse tree of the notation, never a graph of kernel data.
  • Zero authoritycompile() has no imports and no I/O (pure). {$ref} threads the sealed id an earlier step returned into a later payload — what an app does by hand (const t = await create(); link(t.id, …)), not evaluation of kernel data. Every effect goes through the governed door.
  • Genome plane deferred honestly — a ⟦genome⟧ header or σ/α/γ strand throws a typed NotationError('genome_plane_deferred'), not a pretend success.
  • Honest degrade in assemble — substrate rejects recorded per-step (never thrown, never fabricated), dependents abort via skipped_dependency, aggregate ok is truthful.
  • Inspect-then-run split (compile → plan, assemble → run) is exactly right for the untrusted-JWT posture. Suite green (notation 27/27; full 98/98), real-behavior tests over CK.activate + mock, in the house style.

🔧 Two substantive fixes before it lands

1. instance.update payload shape — would fail against live pgCK. The URN-ρ path emits a flat { id, …props }. But ck.js's own contract wraps properties under patchupdate(id, patch) dispatches { id, patch }, and the header notes instance.update {id, patch:{…}} → update_typed … undeclared keys rejected. So assemble's do('instance.update', { id, holds:'x' }) sends holds as an undeclared top-level key → reject. Emit { id, patch: { …props } }. This path is untested (0 instance.update in the suite) — please add a URN-ρ case to lock the wire shape. (create is correctly flat — create_typed takes {type, …fields} — so only update needs this.)

2. Domain-type IRI resolution hardcodes v3.8 core. resolveType sends a bare name to …/ontology/v3.8/core#<Name>. Two issues: (a) it pins v3.8 while we are CKP v3.9, and those v3.8 ontology IRIs currently 404; (b) a kernel's domain types are its own declared class IRIs (urn:ckp:<project>/type/<Name>), not the protocol core-vocabulary namespace — so bare-name→core emits a type no declared shape matches (the mock accepts it; real create_typed will not). Suggest: keep the core namespace for protocol vocab only, and for domain types require a full IRI/CURIE or make the base injectable (compile(source, { typeBase })); do not pin a version that 404s.

🎛 One design call (you flagged it) — do vs named methods

Running each step through handle.do(verb, payload) keeps the plan a uniform, inspectable [{verb,payload}] — I want to keep it. But it makes the compiler responsible for each verb's exact wire payload, which is precisely how the update nesting slipped (the named update() would have wrapped it for free). So if we keep do (my preference), the rule becomes: every verb's payload is contract-tested against ck.js's shapes — turning the choice into a test obligation, not a latent trap.

🧹 Minor

  • Fixture naming: the shipped test uses a specific consumer's kernel name — please use a generic fixture (Demo.Session / Board, matching the demo/generic convention in the other smoke tests). Keeps public fixtures neutral.
  • ;-comment stripping runs before quote parsing, so a ; inside a quoted value is truncated — edge case; guard or note.
  • τ emits no evidence; fine for slice 1, but transition(id, to, evidence) takes it — a future arg.
  • verified: reply.verified ?? undefined writes an explicit undefined key — cosmetic.

Disposition

Accepting the direction and the shape — right design, respects the invariants. Not merge-as-is only for the two wire fixes (update patch-nesting + type resolution) and the fixture rename; land those with a URN-ρ test and it rides as its own cut after v1.5.4, exactly as scoped on #7. Thanks for handing it over so cleanly for cklib to own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request P1 Priority 1 — this release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant